#include "VideoProcessor.h"
#include "core/gamestream/GameStreamManager.h"
#include "core/gamestream/XComponentBackend.h"
#include "core/utils/AVUtils.h"
#include <cstdint>

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN  0x0
#define LOG_TAG     "VideoDecoderCallbacks"
#include <hilog/log.h>


using VideoDecoderCallbacks = VideoProcessor::VideoDecoderCallbacks;

void VideoDecoderCallbacks::onError(OH_AVCodec *codec, int32_t errorCode, void *userData) {
    OH_LOG_ERROR(LOG_APP, "Video decoder error %{public}d", errorCode);
    GameStreamManager::emitEvent("VideoDecodeError", {errorCode});
}

void VideoDecoderCallbacks::onStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
    OH_LOG_INFO(LOG_APP, "Video decoder detected stream changed");
    // TODO handle stream change
}

void VideoDecoderCallbacks::onNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    if constexpr (DEBUG) {
        OH_LOG_DEBUG(LOG_APP, "Video decoder need input buffer no %{public}d", index);
    }
    auto &self = *reinterpret_cast<VideoProcessor*>(userData);
    /* inject buffer uuid for tracing */
    const decltype(AVUtils::PtsAsBufferCtx::uuid) bufferUuid = random();
    do {
        OH_AVCodecBufferAttr attr;
        AV_CALL_OR_ELSE(OH_AVBuffer_GetBufferAttr(buffer, &attr), break);
        attr.pts = [&] {
            AVUtils::PtsAsBufferCtx ctx;
            ctx.uuid = bufferUuid;
            return *reinterpret_cast<decltype(attr.pts)*>(&ctx);
        }();
        AV_CALL_OR_ELSE(OH_AVBuffer_SetBufferAttr(buffer, &attr), break);
    } while (0);
    {
        std::unique_lock l(self.decodeMtx);
        self.decodeQueue.push_back(AVBufferPair{ .index = index, .buffer = buffer, .healthy = true });
    }
    self.decodeReadyCond.notify_one();
    GameStreamManager::videoStats.reportRequested(bufferUuid);
}

void VideoDecoderCallbacks::onNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    if constexpr (DEBUG) {
        OH_LOG_DEBUG(LOG_APP, "Video decoder buffer no %{public}d ready for output", index);
    }
    auto &self = *reinterpret_cast<VideoProcessor*>(userData);
    const auto bufferUuid = [&] () -> decltype(AVUtils::PtsAsBufferCtx::uuid) {
        OH_AVCodecBufferAttr attr;
        AV_CALL_OR_ELSE(OH_AVBuffer_GetBufferAttr(buffer, &attr), return 0);
        return reinterpret_cast<AVUtils::PtsAsBufferCtx*>(&attr.pts)->uuid;
    }();
    {
        std::unique_lock l(self.renderMtx);
        self.renderQueue.push_back(AVBufferPair{ .index = index, .buffer = buffer });
    }
    self.renderReadyCond.notify_one();
    GameStreamManager::videoStats.reportDecoded(bufferUuid);
}
